using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SqlServer.Dts.Runtime;
using System.Xml;

namespace Microsoft.SharedSource.SqlServer.MDDE.Components
{
    [
        System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased"),
        SupportedComponentType("STOCK:SEQUENCE")
    ]
    class MDDESequenceContainer : MDDEContainer
    {
        #region Member Variables

        //private MDDEExecutionDAG innerExecutionDAG;

        private XmlNodeList changedProperties = null;
        private Sequence component = null;

        #endregion

        public MDDESequenceContainer(Package package, Object component, string name, string uri)
            : base(package, component, name, uri)
        {
            if (!(component is Sequence))
                throw new ArgumentException();
        }

        #region Overridden Members

        public override void GetChangeXml(MDDEComponent subject, XmlDocument changeXml)
        {
            #region Set Values

            if (subject == null)
                throw new ArgumentNullException("Subject can not NULL");

            if (!(subject is MDDESequenceContainer))
                throw new ArgumentException("Subject is not of type MDDESequenceContainer");

            component = (Sequence)innerComponent;
            Sequence referenceComponent = (Sequence)subject.InnerComponent;

            DtsProperties componentPropertyCollection = (DtsProperties)component.Properties;
            DtsProperties referenceComponentPropertyCollection = (DtsProperties)referenceComponent.Properties;

            XmlNode componentNode = null;
            componentNode = changeXml.CreateElement("Component");

            bool nodeEmpty = true;

            #endregion

            #region Compare properties of Component with subject and create Properties' Xml

            for (int propertyIndex = 0; propertyIndex < componentPropertyCollection.Count; propertyIndex++)
            {
                if (componentPropertyCollection[propertyIndex].Name.CompareTo("Name") == 0)
                    continue;

                DtsProperty componentProperty = componentPropertyCollection[propertyIndex];
                DtsProperty referenceComponentProperty = referenceComponentPropertyCollection[propertyIndex];

                if ((componentProperty.Get && componentProperty.Set)
                    && (referenceComponentProperty.Get && referenceComponentProperty.Set))
                {
                    string componentPropertyValue = String.Empty;
                    if (componentProperty.GetValue(component) != null)
                        componentPropertyValue = componentProperty.GetValue(component).ToString();

                    string referenceComponentPropertyValue = String.Empty;
                    if (referenceComponentProperty.GetValue(referenceComponent) != null)
                        referenceComponentPropertyValue = referenceComponentProperty.GetValue(referenceComponent).ToString();

                    if (componentPropertyValue != referenceComponentPropertyValue)
                    {
                        UpdateNodeProperties(changeXml, componentNode, componentProperty.Name, componentPropertyValue);
                        nodeEmpty = false;
                    }
                }
            }

            #endregion

            #region Update Changes Xml

            if (!nodeEmpty)
            {
                AddAttribute(changeXml, componentNode, "Name", this.name);
                AddAttribute(changeXml, componentNode, "URL", this.uri);
                AddAttribute(changeXml, componentNode, "RowsetDescriptor", "");

                changeXml.FirstChild.AppendChild(componentNode);
            }

            #endregion

            #region Trace Successors and Update Changes Xml

            this.innerExecutionDAG.GetChangeXml(((MDDESequenceContainer)subject).innerExecutionDAG, changeXml);

            #endregion
        }

        public override bool ApplyChanges(XmlDocument changeXml, MDDEExecutionDAG originalPackage)
        {
            try
            {
                #region Apply Changes to Inner Execution DAG

                this.innerExecutionDAG.ApplyChanges(changeXml, originalPackage);

                #endregion

                #region Set Values

                XmlNode componentChangeXml =
                    changeXml.SelectSingleNode("//PackageChanges/Component[@URL='" + this.uri + "']");

                //if (innerComponent == null)
                //    return false;

                //if (!(innerComponent is Sequence))
                //    return false;

                component = (Sequence)innerComponent;

                DtsProperties dtsProperties = (DtsProperties)component.Properties;

                #endregion

                #region Read Change Xml

                if (componentChangeXml != null)
                {
                    if (componentChangeXml.SelectSingleNode("Properties") != null)
                        changedProperties = componentChangeXml["Properties"].ChildNodes;
                }

                #endregion

                #region Apply Properties and Verify If Sufficient Property Values' Are Available

                if (componentChangeXml != null)
                {
                    ApplyProperties(dtsProperties);
                }

                #endregion
                                
                return true;
            }

            catch
            {
                // TODO: Add to Log
                return false;
            }
        }

        public override XmlDocument GetComponentTree()
        {
            // Create a tree document.
            XmlDocument componentTree = base.GetComponentTree();

            // Clear off the attributes on Root node.
            XmlElement xRoot = componentTree.DocumentElement;
            xRoot.Attributes.RemoveAll();

            // Add in fresh attributes.
            AppendAttribute(xRoot,
                new string[]{"Type", "Executable", "Name", Name, "Uri", Uri,
                    "ClassId", "STOCK:SEQUENCE", "RowsetDescriptorType", string.Empty,
                    "ContainsMappings", "False", "ContainsCollections", "False" });

            // Add the 'Executables' node.
            XmlElement xExecutables = AppendChildElement(componentTree.DocumentElement, "Node",
                new string[]{"Type", "Collection","Name", "Executables","Uri", Uri + "/Executables",
                    "ClassId", string.Empty,"RowsetDescriptorType", string.Empty,
                    "ContainsMappings", "False", "ContainsCollections", "False" });

            // Add the 'Precedence Constraints' node.
            XmlElement xConstraints = AppendChildElement(componentTree.DocumentElement, "Node",
                new string[]{"Type", "Collection", "Name", "Precedence Constraints", 
                    "Uri", Uri + "/Precedence Constraints", "ClassId", string.Empty, 
                    "RowsetDescriptorType", string.Empty, "ContainsMappings", "False", 
                    "ContainsCollections", "False" });

            // Traverse the execution DAG in execution order.
            foreach (MDDEComponent graphNode in this.innerExecutionDAG)
            {
                // Append the successor tree to appropriate parent node.

                // Check for precedence constraints.
                if (graphNode is MDDEPrecedenceConstraint)
                {
                    // Get the component tree of node.
                    XmlDocument xSuccessorTree = graphNode.GetComponentTree();

                    // Append the child tree to 'Event Handlers' node.
                    xConstraints.AppendChild(
                        componentTree.ImportNode(
                        xSuccessorTree.DocumentElement, true));
                }

                // Check for Executables.
                if (graphNode is MDDEContainer)
                {
                    // Get the component tree of node.
                    XmlDocument xSuccessorTree = graphNode.GetComponentTree();

                    // Append the child tree to 'Variables' node.
                    xExecutables.AppendChild(
                        componentTree.ImportNode(
                        xSuccessorTree.DocumentElement, true));
                }
            }

            return componentTree;
        }

        #endregion

        #region Helper Methods

        protected void ApplyProperties(DtsProperties dtsProperties)
        {
            if (changedProperties != null)
            {
                foreach (XmlNode changedProperty in changedProperties)
                {
                    dtsProperties[changedProperty.Attributes["Name"].Value].SetValue(component,
                        ConvertToCompatibleType(changedProperty.Attributes["Value"].Value, dtsProperties[changedProperty.Attributes["Name"].Value].Type.ToString()));
                }
            }
        }

        #endregion
    }
}
